package xyz.hanks.library.bang;

import xyz.hanks.library.bang.ResourceTable;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorGroup;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.*;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;
import ohos.media.image.PixelMap;

import java.io.IOException;
import java.util.List;


/**
 * Created by hanks.
 */
public class SmallBangView extends StackLayout {

  private Image icon;
  private Text text;
  private DotsView dotsView;
  private CircleView circleView;
  private Icon currentIcon;
  private OnAnimationEndListener animationEndListener;
  private int circleStartColor;
  private int circleEndColor;
  private int dotPrimaryColor = 0;
  private int dotSecondaryColor = 0;

  private int iconSize;
  private boolean isShow = true;
  private int textSize = 50;
  private String textFont = "hanks";
  private int getTextSize;


  private float animationScaleFactor;

  private boolean isChecked;


  private boolean isEnabled;
  private AnimatorGroup animatorSet;

  private PixelMapElement likeDrawable;
  private PixelMapElement unLikeDrawable;

  public SmallBangView(Context context) {
    this(context, null);
  }

  public SmallBangView(Context context, AttrSet attrs) {
    this(context, attrs, "");
  }

  public SmallBangView(Context context, AttrSet attrs, String defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs, defStyleAttr);
  }

  /**
   * Does all the initial setup of the button such as retrieving all the attributes that were
   * set in xml and inflating the like button's view and initial state.
   *
   * @param context
   * @param attrs
   * @param defStyle
   */
  private void init(Context context, AttrSet attrs, String defStyle) {
//    setClickedListener(this);
//        setTouchEventListener(this);
    Component root = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_likeview, this, true);
    icon = (Image) root.findComponentById(ResourceTable.Id_icon);
    text = (Text) root.findComponentById(ResourceTable.Id_text);
    getTextSize = text.getTextSize();
    dotsView = (DotsView) root.findComponentById(ResourceTable.Id_dots);
    circleView = (CircleView) root.findComponentById(ResourceTable.Id_circle);
    if (attrs.getAttr("icon_size").isPresent()) {
      iconSize = attrs.getAttr("icon_size").get().getDimensionValue();
    } else {
      iconSize = (int) Utils.dipToPixels(context, 40);
    }
    if (attrs.getAttr("icon_type").isPresent()) {
      String iconType = attrs.getAttr("icon_type").get().getStringValue();
      if (!iconType.isEmpty()) {
        currentIcon = parseIconType(iconType);
      }
    }

    if (attrs.getAttr("icon_show").isPresent()) {
      isShow = attrs.getAttr("icon_show").get().getBoolValue();
    }

    if (attrs.getAttr("text_text").isPresent()) {
      textFont = attrs.getAttr("text_text").get().getStringValue();
    }


    if (attrs.getAttr("text_size").isPresent()) {
      textSize = attrs.getAttr("text_size").get().getIntegerValue();
    }

    text.setText(textFont);
    if (isShow) {
      icon.setVisibility(VISIBLE);
      text.setVisibility(INVISIBLE);
    } else {
      icon.setVisibility(INVISIBLE);
      text.setVisibility(VISIBLE);
      iconSize = textSize;
    }

    if (attrs.getAttr("like_drawable").isPresent()) {
      likeDrawable = (PixelMapElement) attrs.getAttr("like_drawable").get().getElement();
      if (likeDrawable != null)
        setLikeDrawable(likeDrawable);
    }
    if (attrs.getAttr("unlike_drawable").isPresent()) {
      unLikeDrawable = (PixelMapElement) attrs.getAttr("unlike_drawable").get().getElement();
      if (unLikeDrawable != null)
        setUnlikeDrawable(unLikeDrawable);
    }

    if (attrs.getAttr("circle_start_color").isPresent()) {
      circleStartColor = attrs.getAttr("circle_start_color").get().getColorValue().getValue();
      circleView.setStartColor(circleStartColor);
    }


    if (attrs.getAttr("circle_end_color").isPresent()) {
      circleEndColor = attrs.getAttr("circle_end_color").get().getColorValue().getValue();
      circleView.setEndColor(circleEndColor);
    }
    if (attrs.getAttr("dots_primary_color").isPresent()) {
      dotPrimaryColor = attrs.getAttr("dots_primary_color").get().getColorValue().getValue();
    }
    if (attrs.getAttr("dots_secondary_color").isPresent()) {
      dotSecondaryColor = attrs.getAttr("dots_secondary_color").get().getColorValue().getValue();
    }

    if (dotPrimaryColor != 0 && dotSecondaryColor != 0) {
      dotsView.setColors(dotPrimaryColor, dotSecondaryColor);
    }


    if (likeDrawable == null && unLikeDrawable == null) {
      if (currentIcon != null) {
        setIcon();
      } else {
        setIcon(IconType.Heart);
      }
    }

    if (attrs.getAttr("is_enabled").isPresent()) {
      setEnabled(attrs.getAttr("is_enabled").get().getBoolValue());
    } else {
      setEnabled(true);
    }

    if (attrs.getAttr("liked").isPresent()) {
      setLiked(attrs.getAttr("liked").get().getBoolValue());
    } else {
      setLiked(false);
    }

    if (attrs.getAttr("anim_scale_factor").isPresent()) {
      setAnimationScaleFactor(attrs.getAttr("anim_scale_factor").get().getFloatValue());
    } else {
      setAnimationScaleFactor(3);
    }

  }

  /**
   * setIsShow: 设置是否显示图片
   */
  public void setIsShow(boolean show) {
    isShow = show;
    if (isShow) {
      icon.setVisibility(VISIBLE);
      text.setVisibility(INVISIBLE);
    } else {
      icon.setVisibility(INVISIBLE);
      text.setVisibility(VISIBLE);
      iconSize = textSize;
    }
  }

  /**
   * setTextFont: 设置文字
   */
  public void setTextFont(String font) {
    textFont = font;
    text.setText(textFont);
  }

  /**
   * setTextSize: 设置文字大小
   */
  public void setTextSize(int size) {
    textSize = size;
    text.setTextSize(textSize);
  }

  /**
   * This drawable is shown when the button is a liked state.
   *
   * @param resId
   */
  public void setLikeDrawableRes(int resId) {
    icon.setPixelMap(resId);
    if (iconSize != 0) {
      PixelMap pixelMap = icon.getPixelMap();
      likeDrawable = new PixelMapElement(pixelMap);
      likeDrawable.setBounds(0, 0, iconSize, iconSize);
      icon.setWidth(iconSize);
      icon.setHeight(iconSize);
      icon.setImageElement(likeDrawable);
      text.setTextSize(iconSize);

    }
  }


  /**
   * This drawable is shown when the button is in a liked state.
   *
   * @param likeDrawable
   */
  public void setLikeDrawable(PixelMapElement likeDrawable) {
    if (iconSize != 0) {
      likeDrawable.setBounds(0, 0, iconSize, iconSize);
      icon.setWidth(iconSize);
      icon.setHeight(iconSize);
      text.setTextSize(iconSize);
    }
    this.likeDrawable = likeDrawable;

    if (isChecked) {
      icon.setImageElement(this.likeDrawable);
    }
  }

  /**
   * This drawable will be shown when the button is in on unLiked state.
   *
   * @param resId
   */
  public void setUnlikeDrawableRes(int resId) {
    icon.setPixelMap(resId);
    if (iconSize != 0) {
      PixelMap pixelMap = icon.getPixelMap();
      unLikeDrawable = new PixelMapElement(pixelMap);
      unLikeDrawable.setBounds(0, 0, iconSize, iconSize);
      icon.setWidth(iconSize);
      icon.setHeight(iconSize);
      text.setTextSize(iconSize);
      icon.setImageElement(unLikeDrawable);
    }
  }

  /**
   * This drawable will be shown when the button is in on unLiked state.
   *
   * @param unLikeDrawable
   */
  public void setUnlikeDrawable(PixelMapElement unLikeDrawable) {
    if (iconSize != 0) {
      unLikeDrawable.setBounds(0, 0, iconSize, iconSize);
      icon.setWidth(iconSize);
      icon.setHeight(iconSize);
      text.setTextSize(iconSize);
    }
    this.unLikeDrawable = unLikeDrawable;

    if (!isChecked) {
      icon.setImageElement(this.unLikeDrawable);
    }
  }

  /**
   * Sets one of the three icons that are bundled with the library.
   *
   * @param currentIconType
   */
  public void setIcon(IconType currentIconType) {
    currentIcon = parseIconType(currentIconType);
    setLikeDrawableRes(currentIcon.getOnIconResourceId());
    setUnlikeDrawableRes(currentIcon.getOffIconResourceId());
  }

  public void setIcon() {
    setLikeDrawableRes(currentIcon.getOnIconResourceId());
    setUnlikeDrawableRes(currentIcon.getOffIconResourceId());
  }

  /**
   * Sets the size of the drawable/icon that's being used. The views that generate
   * the like effect are also updated to reflect the size of the icon.
   *
   * @param iconSize
   */

  public void setIconSizeDp(int iconSize) {
    setIconSizePx((int) Utils.dipToPixels(getContext(), (float) iconSize));
  }

  /**
   * Sets the size of the drawable/icon that's being used. The views that generate
   * the like effect are also updated to reflect the size of the icon.
   *
   * @param iconSize
   */
  public void setIconSizePx(int iconSize) {
    this.iconSize = iconSize;
    setEffectsViewSize();
    this.unLikeDrawable.setBounds(0, 0, iconSize, iconSize);
    this.likeDrawable.setBounds(0, 0, iconSize, iconSize);
    icon.setWidth(iconSize);
    icon.setHeight(iconSize);
    text.setTextSize(iconSize);
  }

  /**
   * * Parses the specific icon based on string
   * version of its enum.
   * These icons are bundled with the library and
   * are accessed via objects that contain their
   * resource ids and an enum with their name.
   *
   * @param iconType
   * @return Icon
   */
  private Icon parseIconType(String iconType) {
    List<Icon> icons = Utils.getIcons();

    for (Icon icon : icons) {
      if (icon.getIconType().name().toLowerCase().equals(iconType.toLowerCase())) {
        return icon;
      }
    }

    throw new IllegalArgumentException("Correct icon type not specified.");
  }

  /**
   * Parses the specific icon based on it's type.
   * These icons are bundled with the library and
   * are accessed via objects that contain their
   * resource ids and an enum with their name.
   *
   * @param iconType
   * @return
   */
  private Icon parseIconType(IconType iconType) {
    List<Icon> icons = Utils.getIcons();

    for (Icon icon : icons) {
      if (icon.getIconType().equals(iconType)) {
        return icon;
      }
    }

    throw new IllegalArgumentException("Correct icon type not specified.");
  }

  /**
   * Listener that is triggered once the
   * button animation is completed
   *
   * @param animationEndListener
   */
  public void setOnAnimationEndListener(OnAnimationEndListener animationEndListener) {
    this.animationEndListener = animationEndListener;
  }


  /**
   * This set sets the colours that are used for the little dots
   * that will be exploding once the like button is clicked.
   *
   * @param primaryColor
   * @param secondaryColor
   */
  public void setExplodingDotColorsRes(int primaryColor, int secondaryColor) {
    try {
      dotsView.setColors(getResourceManager().getElement(primaryColor).getColor(), getResourceManager().getElement(secondaryColor).getColor());
    } catch (IOException e) {
      e.printStackTrace();
    } catch (NotExistException e) {
      e.printStackTrace();
    } catch (WrongTypeException e) {
      e.printStackTrace();
    }
  }

  public void setExplodingDotColorsInt(int primaryColor, int secondaryColor) {
    dotsView.setColors(primaryColor, secondaryColor);
  }

  public void setCircleStartColorRes(int circleStartColor) {
    try {
      this.circleStartColor = getResourceManager().getElement(circleStartColor).getColor();
    } catch (IOException e) {
      e.printStackTrace();
    } catch (NotExistException e) {
      e.printStackTrace();
    } catch (WrongTypeException e) {
      e.printStackTrace();
    }
    circleView.setStartColor(this.circleStartColor);
  }

  public void setCircleStartColorInt(int circleStartColor) {
    this.circleStartColor = circleStartColor;
    circleView.setStartColor(circleStartColor);
  }

  public void setCircleEndColorRes(int circleEndColor) {
    try {
      this.circleEndColor = getResourceManager().getElement(circleEndColor).getColor();
    } catch (IOException e) {
      e.printStackTrace();
    } catch (NotExistException e) {
      e.printStackTrace();
    } catch (WrongTypeException e) {
      e.printStackTrace();
    }
    circleView.setEndColor(this.circleEndColor);
  }

  /**
   * This function updates the dots view and the circle
   * view with the respective sizes based on the size
   * of the icon being used.
   */
  private void setEffectsViewSize() {
    if (iconSize != 0) {
      dotsView.setSize((int) (iconSize * animationScaleFactor), (int) (iconSize * animationScaleFactor));
      circleView.setSize(iconSize, iconSize);
    }
  }

  public void setStatus(Boolean status) {
    isChecked = status;
    icon.setImageElement(isChecked ? likeDrawable : unLikeDrawable);
  }

  /**
   * Sets the initial state of the button to liked
   * or unliked.
   *
   * @param status
   */
  public void setLiked(Boolean status) {
    isChecked = status;

    text.setTextColor(isChecked ? Color.RED : Color.BLACK);

    icon.setImageElement(isChecked ? likeDrawable : unLikeDrawable);

    if (animatorSet != null) {
      animatorSet.cancel();
    }
    if (isChecked) {
      text.createAnimatorProperty().cancel();
      text.setScaleX(0);
      text.setScaleY(0);
      icon.createAnimatorProperty().cancel();
      icon.setScaleX(0);
      icon.setScaleY(0);
      circleView.setInnerCircleRadiusProgress(0);
      circleView.setOuterCircleRadiusProgress(0);
      dotsView.setCurrentProgress(0);

      animatorSet = new AnimatorGroup();

      AnimatorValue outerCircleAnimator = new AnimatorValue();
      outerCircleAnimator.setDuration(200);
      outerCircleAnimator.setCurveType(Animator.CurveType.DECELERATE);
      outerCircleAnimator.setValueUpdateListener((animatorValue, v) -> {
        circleView.setOuterCircleRadiusProgress(v);
      });

      AnimatorValue innerCircleAnimator = new AnimatorValue();
      innerCircleAnimator.setDuration(200);
      innerCircleAnimator.setDelay(200);
      innerCircleAnimator.setCurveType(Animator.CurveType.DECELERATE);
      innerCircleAnimator.setValueUpdateListener((animatorValue, v) -> {
        circleView.setInnerCircleRadiusProgress(v);
      });

      AnimatorValue starScaleYAnimator = new AnimatorValue();
      starScaleYAnimator.setDuration(250);
      starScaleYAnimator.setDelay(250);
      starScaleYAnimator.setCurveType(Animator.CurveType.OVERSHOOT);
      starScaleYAnimator.setValueUpdateListener((animatorValue, v) -> {
        icon.setScaleY(v);
        text.setScaleY(v);
      });

      AnimatorValue starScaleXAnimator = new AnimatorValue();
      starScaleXAnimator.setDuration(250);
      starScaleXAnimator.setDelay(250);
      starScaleXAnimator.setCurveType(Animator.CurveType.OVERSHOOT);
      starScaleXAnimator.setValueUpdateListener((animatorValue, v) -> {
        icon.setScaleX(v);
        text.setScaleX(v);
      });
      //烟花
      AnimatorValue dotsAnimator = new AnimatorValue();
      dotsAnimator.setDuration(800);
      dotsAnimator.setDelay(50);
      dotsAnimator.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
      dotsAnimator.setValueUpdateListener((animatorValue, v) -> {
        dotsView.setCurrentProgress(v);
      });

      animatorSet.runParallel(
          outerCircleAnimator,
          innerCircleAnimator,
          starScaleYAnimator,
          starScaleXAnimator,
          dotsAnimator
      );

      animatorSet.setStateChangedListener(new Animator.StateChangedListener() {
        @Override
        public void onStart(Animator animator) {
        }

        @Override
        public void onStop(Animator animator) {
        }

        @Override
        public void onCancel(Animator animator) {
          circleView.setInnerCircleRadiusProgress(0);
          circleView.setOuterCircleRadiusProgress(0);
          dotsView.setCurrentProgress(0);
          icon.setScaleX(1);
          icon.setScaleY(1);
          text.setScaleX(1);
          text.setScaleY(1);
        }

        @Override
        public void onEnd(Animator animator) {
          if (animationEndListener != null) {
            animationEndListener.onAnimationEnd(SmallBangView.this);
          }
        }

        @Override
        public void onPause(Animator animator) {
        }

        @Override
        public void onResume(Animator animator) {
        }
      });

      animatorSet.start();
    }
  }

  /**
   * Returns current like state
   *
   * @return current like state
   */
  public boolean isLiked() {
    return isChecked;
  }

  @Override
  public void setEnabled(boolean enabled) {
    isEnabled = enabled;
  }

  /**
   * Sets the factor by which the dots should be sized.
   */
  public void setAnimationScaleFactor(float animationScaleFactor) {
    this.animationScaleFactor = animationScaleFactor;

    setEffectsViewSize();
  }


}
